home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.mactech.com 2010
/
ftp.mactech.com.tar
/
ftp.mactech.com
/
challenge
/
13.08
/
ChallengeEqEval.sit.hqx
/
Challenge, Equation Evaluator
/
Evaluate.c
< prev
next >
Wrap
Text File
|
1997-06-25
|
15KB
|
723 lines
//
// Evaluate.c
//
// MacTech Programmer's Challenge entry for May, 1997
// Written by Mark Day
//
// Contains routines to parse an equation and drive the
// evaluation of that equation over a set of input values.
#include <ctype.h>
#include <string.h>
#include <math.h>
#undef HUGE_VAL // fp.h redefines this
#include <fp.h>
#include <Memory.h>
#include <OSUtils.h>
#include "Evaluate.h"
ulong gVariableFlags;
struct TokenLookup {
int token;
int which;
char *string;
};
typedef struct TokenLookup TokenLookup;
TokenLookup gTokenLookup[] = {
tokLeftParen, kLeftParen, "(",
tokRightParen, kRightParen, ")",
tokAddOp, kAdd, "+",
tokAddOp, kSubtract, "-",
tokMulOp, kMultiply, "*",
tokMulOp, kDivide, "/",
tokPowerOp, kExponent, "^",
tokRootOp, kFuncSquareRoot,"\\",
tokFactorial, kFuncFactorial, "!",
tokFunction, kFuncLogN, "ln",
tokFunction, kFuncLogTen, "log",
// Hyperbolic functions need to come first or else they
// would parse as the normal functions plus an extra
// "h" character (which makes for a syntax error)
tokFunction, kFuncSinh, "sinh",
tokFunction, kFuncCosh, "cosh",
tokFunction, kFuncTanh, "tanh",
tokFunction, kFuncSech, "sech",
tokFunction, kFuncCsch, "csch",
tokFunction, kFuncCoth, "coth",
tokFunction, kFuncSin, "sin",
tokFunction, kFuncCos, "cos",
tokFunction, kFuncTan, "tan",
tokFunction, kFuncSec, "sec",
tokFunction, kFuncCsc, "csc",
tokFunction, kFuncCot, "cot",
tokFunction, kFuncArcSin, "asin",
tokFunction, kFuncArcCos, "acos",
tokFunction, kFuncArcTan, "atan",
tokFunction, kFuncArcSec, "asec",
tokFunction, kFuncArcCsc, "acsc",
tokFunction, kFuncArcCot, "acot",
tokFunction, kFuncAbs, "abs",
tokVariable, kPi, "p",
tokVariable, kE, "e",
tokVariable, kR, "r",
tokVariable, kTheta, "t",
tokVariable, kX, "x",
tokVariable, kY, "y",
tokVariable, kZ, "z",
tokVariable, kN, "n",
tokAssign, kEquals, "=",
tokInvalid, kInvalidToken, "",
};
//
// These are single precision versions of functions, not
// found in either math.h or fp.h
//
static float Factorial(float x) { return gamma(x+1.0); }
static float Secant(float x) { return 1.0 / cosf(x); }
static float CoSecant(float x) { return 1.0 / sinf(x); }
static float CoTangent(float x) { return 1.0 / tanf(x); }
static float ArcSecant(float x) { return acosf(1.0/x); }
static float ArcCoSecant(float x) { return asinf(1.0/x); }
static float ArcCoTangent(float x) { return atanf(1.0/x); }
static float HypSecant(float x) { return 1.0 / coshf(x); }
static float HypCoSecant(float x) { return 1.0 / sinhf(x); }
static float HypCoTangent(float x) { return 1.0 / tanhf(x); }
typedef float (*FloatFunc)(float);
FloatFunc gFunctions[] = {
(FloatFunc) powf,
fabsf,
sqrtf,
Factorial,
expf,
logf,
log10f,
sinf,
cosf,
tanf,
Secant,
CoSecant,
CoTangent,
asinf,
acosf,
atanf,
ArcSecant,
ArcCoSecant,
ArcCoTangent,
sinhf,
coshf,
tanhf,
HypSecant,
HypCoSecant,
HypCoTangent,
(FloatFunc) atan2f
};
//
// Return the next token in the equation. Adjust the
// pointer to point just after the token.
//
int GetToken(char **equation, Constant *value)
{
int token = tokInvalid;
char *s = *equation;
int i;
int width;
TokenLookup *p;
//
// skip leading white space
//
while (*s == ' ' || *s == '\t' || *s == '\n')
++s;
if (*s == '\0')
return tokInvalid;
//
// See if *s matches one of the token strings
//
p = gTokenLookup;
while (p->token != tokInvalid) {
char *tokenString = p->string;
size_t len = strlen(tokenString);
if (!strncmp(s, tokenString, len)) {
token = p->token;
value->which = p->which;
s += len;
break;
}
else {
++p;
}
}
//
// Update the current position within the string.
//
*equation = s;
//
// Try to parse a numeric constant.
//
if (token == tokInvalid) {
token = GetNumberToken(equation, value);
}
return token;
}
//
// GetNumberToken recognizes numeric constants of the forms
// [0-9]+.[0-9]* or [0-9]*.[0-9]+
//
int GetNumberToken(char **equation, Constant *value)
{
char *s;
char c; // keep the next character in a register
long digits = 0; // used to determine whether we've seen any digits yet
float val; // the value of the constant
float divisor; // keeps track of place value of float digits
int token; // the kind of number we found
token = tokInvalid; // assume we didn't find a number
s = *equation; // point to start of string
c = *s; // get first char
//
// Gather the integer part of the constant
//
val = 0.0;
while (isdigit(c)) {
val = val * 10.0 + (c - '0'); // shift in the next digit
++digits; // remember we found a digit
c = *(++s); // get next character
}
//
// If we found an integer part, prepare to return it.
//
if (digits) {
value->f = val;
token = tokFloat;
}
//
// Now let's see if there is a trailing decimal point
// and optional digits. If so, then this is a floating
// point constant.
//
if (c == '.') {
c = *(++s); // skip over decimal point
divisor = 1.0; // haven't seen fraction part yet
while (isdigit(c)) {
val = val * 10.0 + (c - '0'); // shift in next digit
divisor *= 10; // remember to fix place value
++digits; // we saw another digit
c = *(++s); // get next character
}
//
// If there were digits before or after the decimal
// point, then we have a valid float. Otherwise,
// we only saw a decimal point.
//
if (digits) {
value->f = val / divisor; // this fixes the place value
token = tokFloat;
}
else {
// We saw only a decimal point. Point us back at the decimal point.
--s;
}
}
*equation = s; // point beyond what we found
return token; // return what we found
}
TokenNode *NewTokenNode(TokenNode *left, TokenNode *right, int token)
{
TokenNode *node;
node = (TokenNode *) NewPtr(sizeof(TokenNode));
node->left = left;
node->right = right;
node->token = token;
return node;
}
void FreeTree(TokenNode *root)
{
if (root) {
FreeTree(root->left);
FreeTree(root->right);
DisposePtr((Ptr) root);
}
}
/*
A simple parser for our expression grammar. The grammar
looks like:
expression -> term ( ADD_OP term )*
term -> factor ( [ MULTIPLY_OP ] factor )*
factor -> SQUARE_ROOT factor
| signed EXPONENT factor
| signed
signed -> MINUS signed
| PLUS signed
| gamma
gamma -> simple_value ( FACTORIAL )*
simple_value-> NUMBER
| VARIABLE
| LEFT_PAREN expression RIGHT_PAREN
| FUNCTION LEFT_PAREN expression RIGHT_PAREN
statement -> VARIABLE EQUALS expression
where (...)* means repeated zero or more times, and
[...] means optional.
*/
TokenNode *ParseExpression(char **s)
{
char *temp;
TokenNode *left, *right, *node;
int token;
Constant value;
node = NULL;
// Get the first term
left = ParseTerm(s);
if (left == NULL)
return left;
node = left;
// Get successive terms, separated by tokAddOp's
do {
temp = *s;
token = GetToken(&temp, &value);
if (token == tokAddOp) {
*s = temp;
right = ParseTerm(s);
if (right) {
node = NewTokenNode(left, right, token);
node->value.which = value.which;
left = node; // this makes it left-associative
}
}
} while (token == tokAddOp);
return node;
}
TokenNode *ParseTerm(char **s)
{
char *temp;
TokenNode *left, *right, *node;
int token;
Constant value;
node = NULL;
left = ParseFactor(s);
if (left == NULL)
return left;
node = left;
//
// Get successive factors. They may be separated by
// tokMulOp's, or they may be adjacent (implying
// multiplication). If we find a tokAddOp, then we
// have to unwind; otherwise, an expression like
// "3 - 7" would be parsed as "3 * (-7)".
//
do {
temp = *s;
token = GetToken(&temp, &value);
switch (token) {
// Explicit multiply/divide
case tokMulOp:
*s = temp;
right = ParseFactor(s);
if (right) {
node = NewTokenNode(left, right, token);
node->value.which = value.which;
left = node; // this makes it left-associative
}
break;
// Explicit add/subtract; don't do implicit multiply.
// Use the single factor only.
case tokAddOp:
node = left;
break;
// Implicit multiply or single factor
default:
// Try to find a second factor
temp = *s;
right = ParseFactor(s);
if (right) {
// Got one, so do implicit multiply
token = tokMulOp; // pretend there was an explicit multiply
node = NewTokenNode(left, right, token);
node->value.which = kMultiply;
left = node; // make it left-associative
}
else {
// Nope, just a single factor
*s = temp; // put back the tokens ParseFactor gobbled
node = left;
}
break;
}
} while (token == tokMulOp);
return node;
}
TokenNode *ParseFactor(char **s)
{
char *temp;
TokenNode *left, *right, *node;
int token;
Constant value;
node = NULL;
//
// See if this is a (unary) square root
//
temp = *s;
token = GetToken(&temp, &value);
if (token == tokRootOp) {
*s =temp;
left = ParseFactor(s);
if (left) {
// square root is evaluated as a function call
node = NewTokenNode(left, NULL, tokFunction);
node->value.which = kFuncSquareRoot;
}
return node;
}
//
// If we get here, it's either "negative EXPONENT factor" or "negative"
//
left = ParseNegative(s);
if (left == NULL)
return left;
temp = *s;
token = GetToken(&temp, &value);
if (token == tokPowerOp) {
*s = temp;
right = ParseFactor(s);
if (right) {
// We've found u ^ v. If u == e, then convert it to
// a function call, exp(v).
if (left->token == tokVariable && left->value.which == kE) {
node = left; // re-use the node for left
node->token = tokFunction;
node->value.which = kFuncExp;
node->left = right; // set up the function argument
}
else {
// It's an ordinary power, so build a node for it.
node = NewTokenNode(left, right, token);
node->value.which = value.which;
}
}
}
else {
node = left;
}
return node;
}
TokenNode *ParseNegative(char **s)
{
char *temp;
TokenNode *left, *node;
int token;
Constant value;
node = NULL;
left = NULL;
//
// See if this is minus gamma, or gamma.
//
temp = *s;
token = GetToken(&temp, &value);
if (token == tokAddOp && value.which == kSubtract) {
*s =temp;
left = ParseNegative(s);
if (left) {
node = NewTokenNode(left, NULL, tokNegate);
node->value.which = value.which;
}
}
else {
node = ParseFactorial(s);
}
return node;
}
TokenNode *ParseFactorial(char **s)
{
char *temp;
TokenNode *left, *node;
int token;
Constant value;
//
// First, get a simple value.
//
left = ParseSimpleValue(s);
if (left == NULL)
return left;
node = left;
//
// Gather trailing !'s
//
do {
temp = *s;
token = GetToken(&temp, &value);
if (token == tokFactorial) {
*s = temp;
// The factorial operator is converted to a
// function call to the factorial function.
node = NewTokenNode(left, NULL, tokFunction);
node->value.which = kFuncFactorial;
left = node;
}
} while (token == tokFactorial);
return node;
}
TokenNode *ParseSimpleValue(char **s)
{
char *temp;
TokenNode *node, *left;
int token;
Constant value;
left = NULL;
node = NULL;
temp = *s;
token = GetToken(s, &value);
switch (token) {
case tokFloat:
left = NewTokenNode(NULL, NULL, token);
left->value = value;
++gNumConstants;
break;
case tokVariable:
left = NewTokenNode(NULL, NULL, token);
left->value = value;
switch (value.which) {
case kPi:
gVariableFlags |= kPiMask;
break;
case kE:
gVariableFlags |= kEMask;
break;
case kR:
// using r implies using x and y since
// r is computed from x and y
gVariableFlags |= kRMask + kXMask + kYMask;
break;
case kTheta:
// using theta implies using x and y
// since theta is computed from x and y
gVariableFlags |= kThetaMask + kXMask + kYMask;
break;
case kX:
gVariableFlags |= kXMask;
break;
case kY:
gVariableFlags |= kYMask;
break;
case kN:
gVariableFlags |= kNMask;
break;
}
break;
case tokFunction:
node = NewTokenNode(NULL, NULL, token);
node->value.which = value.which;
//
// If the function was abs(), then convert to a
// special token so it can be evaluated more
// efficiently.
//
if (value.which == kFuncAbs)
node->token = tokAbs;
token = GetToken(s, &value);
if (token != tokLeftParen) {
// Missing left parenthesis
node = NULL;
}
// Fall into processing of parethesized expression
case tokLeftParen:
left = ParseExpression(s);
token = GetToken(s, &value); // swallow right parenthesis
break;
case tokRightParen:
// A right parenthesis means we're doing doing
// a recursive parse of an expression. In that
// case, pretend like we hit the end of string.
*s = temp; // back up so caller finds right parenthesis
break;
case tokInvalid:
break;
default:
// An unexpected token was seen
break;
}
//
// If it was a function call, then point to the argument.
//
if (node == NULL)
node = left;
else
node->left = left;
return node;
}
TokenNode *ParseStatement(char **s)
{
TokenNode *node, *left, *right;
int token;
Constant value;
//
// Get the variable
//
token = GetToken(s, &value);
if (token == tokVariable) {
left = NewTokenNode(NULL, NULL, token);
left->value = value;
if (value.which == kZ)
gVariableFlags |= kFunctionOfY; // "z=" implies yP is valid input
}
//
// Get the assignment token
//
token = GetToken(s, &value);
if (token != tokAssign) {
// Missing "="
return NULL;
}
//
// Get the expression
//
right = ParseExpression(s);
//
// Build the node
//
if (right) {
node = NewTokenNode(left, right, token);
node->value.which = value.which;
}
else {
node = NULL;
}
return node;
}
//
// Here's the main entry point for the challenge.
//
void Evaluate(
char *equation, // null-terminated equation to evaluate
const Values *xP, // input values for x
const Values *yP, // input values for y
const IntValues *nP, // input values for n
Results w[]) // preallocated storage for equation values
{
char *s = equation;
TokenNode *root;
Values nFloatValues;
//
// Set up the floating point values of n
//
nFloatValues.first = nP->first;
nFloatValues.delta = nP->delta;
nFloatValues.howMany = nP->howMany;
//
// Parse the input equation
//
gVariableFlags = 0;
root = ParseStatement(&s);
//
// Generate code to evaluate the equation
//
CodeGen(root->right);
MakeDataExecutable(gCodeBase, 32768);
CallGeneratedCode(gCodeBase+4, xP, yP, nP,
&nFloatValues, w,
gConstants, gFunctions,
2.718281828459, 3.1415926535898);
//
// Free up the memory we allocated.
//
DisposePtr((Ptr) gCodeBase);
gCodeBase = NULL;
gNextInstruction = NULL;
FreeTree(root);
if (gConstants) {
DisposePtr((Ptr) gConstants);
gConstants = NULL;
}
}